package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dayatang.utils.DateUtils;
import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.cfg.ConfigContainer;
import com.foreveross.crawl.common.util.DateUtil;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.proxyip.ProxyipProperties;
import com.foreveross.taskservice.common.bean.TaskModel;

/**
 * 美国航空适配器
 * 
 * <pre>
 * 	入口：<a>http://www.americanairlines.cn/intl/cn/index.jsp?locale=zh_CN</a>
 * </pre>
 * 
 * @author fb
 */
@SuppressWarnings({"deprecation", "unchecked"})
public class AmericanairlinesAdapter extends AbstractAdapter {

	private final static Logger log = LoggerFactory.getLogger(AmericanairlinesAdapter.class);
	// 网站请求前缀
	private final static String baseUrl = "http://americanairlines.amadeus.com";
	// 航班详细请求
	private final static String detailUrl = "http://americanairlines.amadeus.com/plnext/AAasiapacific4/Fare.action";
	// 出错后重复次数
	private final static int REPEATE_TIME = 3;
	// 整个过程中的session id
	private String jsessionid = null;
	// js中解析的规则数据
	private Map<String, Object> json = null;

	private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
	
	private SimpleDateFormat curr_sdf = new SimpleDateFormat("yyyy-MM-dd");
	
	private Boolean filterPrice=true;//只查询最低价（开关）
	
	private enum FlightType {
		OUTBOUND,INBOUND;
	}

	public AmericanairlinesAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	@Override
	public String getUrl() throws Exception {
		return "http://americanairlines.amadeus.com/plnext/AAasiapacific4/Override.action";
	}

	@Override
	@SuppressWarnings("rawtypes")
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		List result = null;
		String flightHtml = (String) fetchObject;

		switch (getRouteType()) {
		case INTERNATIONAL_ROUND:
			result = flightInfoCrawl(flightHtml);
		case DOMESTIC_ONEWAYTRIP:
			break;
		case INTERNATIONAL_ONEWAY:
			break;
		default:
			return null;
		}

		return result;
	}

	@Override
	public Object fetch(String url) throws Exception {
		String flightHtml = null;
		url = getUrl();
		String content = errorRepeatExecuteThrowsError(url, getFirstQueryParams(), Reqeust.POST);
		flightHtml = requestAnalysis(content);
		return flightHtml;
	}

	@Override
	public boolean validateFetch(Object fetchObject) throws Exception {
		String flightHtml = (String) fetchObject;
		boolean valid = true;

		if (StringUtils.isBlank(flightHtml)) {
			valid = false;
		} else {
			Document doc = Jsoup.parse(flightHtml);
			// 如果没有些特定样式的html则认为内容不符合要求
			if (doc.select("table.fdff_tableFF").isEmpty()) {
				throw new FlightInfoNotFoundException("可能票己卖完");
			}
		}
		
		return valid;
	}

	private String requestAnalysis(String html) throws Exception {
		if (StringUtils.isBlank(html)) {
			logger.error(String.format(
					"适配器内部重试%s次后抓取内容仍然为空, 当前抓取信息为[%s-%s, %s %s]!", REPEATE_TIME,
					taskQueue.getFromCity(), taskQueue.getToCity(),
					taskQueue.getFlightDate(), taskQueue.getReturnGrabDate()));
			return null;
		}
		
		if (!fristPageValidaIsPass(html)) {
			throw new FlightInfoNotFoundException("没有此航班的信息!");
		}

		Map<String, String> params = new HashMap<String, String>();
		Document doc = Jsoup.parse(html);
		Elements froms = doc.select("form[name=form_select_fare]");
		Element formSelectFare = froms.get(0);
		String action = formSelectFare.attr("action");
		jsessionid = action.split(";")[1];

		for (Node node : formSelectFare.select("> *")) {
			params.put(node.attr("name"), node.attr("value"));
		}

		params.put("DATE_RANGE_VALUE_1", "0");
		params.put("DATE_RANGE_VALUE_2", "0");

		log.debug("美国航空抓取航班信息url：{}, params : {}", "url:" + action, params);

		if (StringUtils.isBlank(params.get("B_DATE_1"))) {
			throw new FlightInfoNotFoundException("没有发现航班信息或己售完");
		}

		return errorRepeatExecuteThrowsError(baseUrl + action, params, Reqeust.POST);
	}

	// 异常重复请求方法
	private String errorRepeatExecute(String url, Map<String, String> params, Reqeust reqeust) {
		String content = null;
		int timeError = 0;
		boolean error = false;

		do {
			try {
				
				content = super.execute(url, params, Reqeust.POST);
			} catch (Exception e) {
				error = true;
				timeError++;
				// 只有出错后才更换ip
				switchProxyipByHttClient();
				log.error("获取美国航空信息第{}次出错:{}", timeError, e.getMessage());
			}
		} while (error && timeError < REPEATE_TIME);

		return content;
	}
	
	// 异常重复请求方法
	private String errorRepeatExecuteThrowsError(String url, Map<String, String> params, Reqeust reqeust) throws Exception {
		String content = null;
		int timeError = 0;
		boolean error = false;

		do {
			try {
				// 只有出错后才更换ip
				content = super.execute(url, params, Reqeust.POST);
			} catch (Exception e) {
				error = true;
				timeError++;
				
				log.error("获取美国航空信息第{}次出错:{}", timeError, e.getMessage());
				
				if (timeError >= REPEATE_TIME) {
					throw e;
				}
				
				switchProxyipByHttClient();
			}
		} while (error || StringUtils.isBlank(content));

		return content;
	}
	
	//第一次进入网站数据验证
	private boolean fristPageValidaIsPass(String html) {
		boolean result = false;
		
		if (StringUtils.isNotBlank(html)) {
			Document doc = Jsoup.parse(html);
			Elements froms = doc.select("form[name=form_select_fare]");
			result = froms.isEmpty() ? false : true;
		}
		
		return result;
	}

	private List<DoublePlaneInfoEntity> flightInfoCrawl(String content) throws Exception {
		List<DoublePlaneInfoEntity> planeInfos = new ArrayList<DoublePlaneInfoEntity>();
		jsonAnalysis(content);
		Map<String, Object> flights = analysisFlight(content);
		List<Map<String, String>> outbound = (List<Map<String, String>>) flights.get("outbound");
		List<Map<String, String>> inbound = (List<Map<String, String>>) flights.get("inbound");
		Map<String, String> binTypes = (Map<String, String>) flights.get("binType");
		Map<String, Object> listRecos = (Map<String, Object>) json.get("listRecos");
		DoublePlaneInfoEntity dpie = null;
		String url = detailUrl + ";" + jsessionid;
		String detailHtml = null;
		

		// 每个去程与每个回程组合后进行不同仓位详细查询
		try{
		for (Map<String, String> outboundInfo : outbound) {
			dpie = null;
	
			for (Map<String, String> inboundInfo : inbound) {
				for (Entry<String, String> binType : binTypes.entrySet()) {
					if (StringUtils.isBlank(binType.getValue())) {
						continue;
					}
					
					if (combinationIsNotExist(outboundInfo.get("id"), inboundInfo.get("id"), binType.getValue())) {
						continue;
					}
					
					// familyID与id的区别  ： familyID: R_0_1而id只有1
					if (radioCombinationIsNotExist(outboundInfo.get("id"), inboundInfo.get("id"), binType.getValue())) {
						continue;
					}
					
					if (dpie == null) {
						dpie = offlineFlightInfoAssembly(outboundInfo);
					}
					
					detailHtml = errorRepeatExecute(url, getFlightPrice(outboundInfo.get("id"), inboundInfo.get("id"), binType.getValue()), Reqeust.POST);
					ReturnDoublePlaneInfoEntity rdpe = offlineFlightReturnInfoAssembly(inboundInfo);
					
					if (StringUtils.isNotBlank(detailHtml) && isValidDetail(detailHtml)) {
						flightDetailAssembly(detailHtml, dpie, rdpe, binType.getKey());
					} else {
						CabinEntity cabin = getOutboundCabin(dpie, binType.getKey());
						dpie.getCabins().add(cabin);
						ReturnCabinEntity rcabin =  PlaneInfoEntityBuilder.buildCabinInfo(binType.getKey(), binType.getKey(), null, null, null, null, null, ReturnCabinEntity.class);
						rdpe.getReturnCabins().add(rcabin);
						CabinRelationEntity cre = new CabinRelationEntity();
						cre.setCabinId(cabin.getId());
						cre.setReturnCabinId(rcabin.getId());
						cre.setTotalFullPrice(getPrice(listRecos, outboundInfo, inboundInfo, binType.getValue().split("\\|")[0]));
						dpie.getCabinRelations().add(cre);
						dpie.getReturnPlaneInfos().add(rdpe);
					}
					
					rdpe.setSumLowestPrice(getPrice(listRecos, outboundInfo, inboundInfo, binType.getValue().split("\\|")[0]));
					rdpe.setSumHighestPrice(rdpe.getSumLowestPrice());
					
				}
			}
			
			if (dpie != null) {
				planeInfos.add(dpie);
			}
		}
		}catch (Exception e) {
			e.printStackTrace();
		}

		PlaneInfoEntityBuilder.buildLimitPrice(planeInfos);
		
		return planeInfos;
	}
	
	private Double getPrice(Map<String, Object> listRecos, Map<String, String> outboundInfo, Map<String, String> inboundInfo, String binType) {
		Map<String, Object> listReco = (Map<String, Object>) listRecos.get(getRecommendationId(binType, outboundInfo.get("id"), inboundInfo.get("id")));
		Map<String, String> price = (Map<String, String>) listReco.get("price");
		return Double.parseDouble(price.get("price").replace("\\u04B0", "").replace(",", ""));
	}
	
	/**
	 * 判断此仓位下，此来回组合是否不存在，如果不存在则反回true。些种判断还末判断全面， 当页面去程按钮触发后，可以回程某些是disable的
	 * 
	 * @param outFamilyID
	 *            去程FamilyID
	 * @param inFamilyID
	 *            回程FamilyID
	 * @param listReco
	 *            某个仓位outbount与inbount对应的关系
	 * @return 【true:不可组可，false:可以组合】
	 */
	private boolean combinationIsNotExist(String outFamilyID, String inFamilyID, String binType) {
		boolean result = true;
		Map<String, Object> listRecos = (Map<String, Object>) json.get("listRecos");
		Map<String, Object> listReco = (Map<String, Object>) listRecos.get(binType.split("\\|")[0]);;
		String outId = "|" + outFamilyID + "|";
		String inId = "|" + inFamilyID + "|";
		String outIds = (String) listReco.get("outboundFlightIDs");
		String InIds = (String) listReco.get("inboundFlightIDs");
		
		if (StringUtils.contains(outIds, outId) && StringUtils.contains(InIds, inId)) {
			result = false;
		}
		
		if (result) {
			log.debug("不通过：binType:{}, outFamilyID:{}, inFamilyID:{}", new Object[]{binType, outFamilyID, inFamilyID});
		}
		
		return result;
	}
	
	/**
	 * 主要用于当前去程是否与所选回程能够组合
	 * 
	 * js代码直接翻译，不要在意细节因为此代码不是本类中逻辑
	 * 被翻译js代码：
	 * <pre>
	 * for (int i = 0; i &lt;= this.MaxIDInbound; i++) {
	 * 	var recoId = this.findMinimumFareFamilyByFlightId(i, 1);
	 * 	var flightIdFound = false;
	 * 	var associatedRecos = this._listRecos[this.SelectedRecommendationID].associatedRecos;
	 * 	if (associatedRecos != null) {
	 * 		for (var k = 0; k &lt; associatedRecos.length; k++) {
	 * 			var lTrueID = associatedRecos[k];
	 * 			if (this._listRecos[lTrueID].inboundFlights[iFlightID] != null) {
	 * 				if (this._listRecos[lTrueID].inboundFlights[iFlightID]
	 * 						.indexOf('|' + i + '|') &gt;= 0) {
	 * 					flightIdFound = true;
	 * 					break;
	 * 				}
	 * 			}
	 * 		}
	 * 	}
	 * 	if (flightIdFound) {
	 * 		this.highlightFlight(i, 1, false, recoId);
	 * 		this.displayNbrOfLastSeat(1, i, this.SelectedRecommendationID);
	 * 	} else {
	 * 		if (this._listRecos[this.SelectedRecommendationID].inboundFlights[iFlightID] != null
	 * 				&amp;&amp; this._listRecos[this.SelectedRecommendationID].inboundFlights[iFlightID]
	 * 						.indexOf('|' + i + '|') &gt;= 0) {
	 * 			this.highlightFlight(i, 1, false, recoId);
	 * 			this.displayNbrOfLastSeat(1, i, this.SelectedRecommendationID);
	 * 		} else {
	 * 			this.grayFlight(i, 1, true);
	 * 			this.hideNbrOfLastSeat(1, i);
	 * 		}
	 * 	}
	 * }
	 * </pre>
	 * 
	 * @param outFamilyID
	 *            出程id
	 * @param inFamilyID
	 *            反程id
	 * @param binType
	 *            仓位radio id
	 * @return 【 treu:不可组合，false:可以组合】
	 */
	private boolean radioCombinationIsNotExist(String outFamilyID, String inFamilyID, String binType) {
		Map<String, Object> listRecos = (Map<String, Object>) json.get("listRecos");
		String selectedRecommendationID = getRecommendationId(binType.split("\\|")[0], outFamilyID, inFamilyID);
		boolean result = true;
		boolean flightIdFound = false;

		flightIdFound = false;
		Map<String, Object> recos = (Map<String, Object>) listRecos.get(selectedRecommendationID);
		List<Integer> associatedRecos = (List<Integer>) recos.get("associatedRecos");

		if (associatedRecos != null) {
			for (Integer lTrueID : associatedRecos) {
				Map<String, Object> recom = (Map<String, Object>) listRecos.get(lTrueID.toString());
				List<String> inboundFlights = (List<String>) recom.get("inboundFlights");

				if (Integer.parseInt(outFamilyID) < inboundFlights.size() && inboundFlights.get(Integer.parseInt(outFamilyID)) != null) {
					// 运行时还是JsonObject，所以要toString
					if (StringUtils.contains(String.valueOf(inboundFlights.get(Integer.parseInt(outFamilyID))), "|" + inFamilyID + "|")) {
						flightIdFound = true;
						break;
					}
				}
			}
		}

		if (flightIdFound) {
			// 显示
			result = false;
		} else {
			List<String> inboundFlights = (List<String>) recos.get("inboundFlights");

			if (Integer.parseInt(outFamilyID) < inboundFlights.size()
					&& inboundFlights.get(Integer.parseInt(outFamilyID)) != null
					&& StringUtils.contains(String.valueOf(inboundFlights
							.get(Integer.parseInt(outFamilyID))), "|" + inFamilyID + "|")) {
				// 显示 
				result = false;
			} else {
				// R_1_i 的全部隐藏
				result = true;
			}
		}

		if (result) {
			log.debug("不通过组合：binType:{}, outFamilyID:{}, inFamilyID:{}", new Object[] { binType, outFamilyID, inFamilyID });
		}

		return result;
	}
	
	/**
	 * 提取抓取页面中的js数据，进行json解析
	 * 
	 * @param html
	 */
	private void jsonAnalysis(String html) {
		Pattern patter  = Pattern.compile(".*?var generatedJSon.*?'(.*).*?'");
		Matcher matcher = patter.matcher(html);
		String jsonStr = null;
		
		if (matcher.find()) {
			jsonStr = matcher.group(1);
			
			if (StringUtils.isNotBlank(jsonStr)) {
				json = JSONObject.fromObject(jsonStr);
			}
		}
	}
	
	// 列表航班解析
	private Map<String, Object> analysisFlight(String flightHtml) {
		 Map<String, Object> flights = new HashMap<String, Object>();
		 List<Map<String, String>> outbound = new ArrayList<Map<String,String>>();
		 List<Map<String, String>> inbound = new ArrayList<Map<String,String>>();
		 Map<String, String> binType = null;
		 
		 List<String> priceList=new ArrayList<String>();
		 Map<String ,Map<String,String>> priceMaps=new HashMap<String, Map<String,String>>();
		 
		 Document doc = Jsoup.parse(flightHtml);
		 Elements tr = doc.select("div#divFFholder > table.tableFFHolder2 tr:eq(1)");		// 获取航班数据区的第二行tr
		 Elements outboundTb =  new Elements();				// 获取去程航班左边的table
		 Elements inboundTb = new Elements(); 				// 获取回程航班右边的table
		 
		 Elements binTypeTd = doc.select("table.fdff_tableFF");								// 仓位类型
		 // 值为29|1等这样形式，前部分29用于确定下面那些去程回程可组合
		 for (Element element : binTypeTd.select("tr:gt(0)")) {
			 binType= new HashMap<String, String>();
			 binType.put(element.select("td:eq(1)").text().trim(), element.select("td:eq(3) input").attr("value")); 
			 String price=element.select("td:eq(3)").text().trim().replaceAll(",", "").replaceAll("Ұ", "");
			 priceList.add(price);
			 priceMaps.put(price, binType);
		 }
		 
		 if(filterPrice && json !=null){//过滤出最低价信息
			 //过滤出最低价格信息
			 Collections.sort(priceList);
			 binType=priceMaps.get(priceList.get(priceList.size()-1));
			 JSONObject  obj =JSONObject.fromObject(json.get("listRecos"));
			 for(Entry<String, String>mapE:binType.entrySet() ){
				 String key= mapE.getValue().split("\\|")[0];
				 String []inbKeys=obj.getJSONObject(key).getString("inboundFlightIDs").replaceFirst("\\|", "").split("\\|");
				 String []outbKeys=obj.getJSONObject(key).getString("outboundFlightIDs").replaceFirst("\\|", "").split("\\|");
				 for(int i=0;i<inbKeys.length;i++){
					Elements e=null;
					String keys=inbKeys[i];
					e=tr.select("td:eq(2) table#right table:gt(0)");
					for(Element es:e){
						String key_s=es.attr("id").split("_")[2];
						if(key_s.equals(keys) ){
							inboundTb.add(es); 
							break;
						}else{
							continue;
						}
					}
					
				 }
				 for(int j=0;j<outbKeys.length;j++){
					Elements e=null;
					String keys=inbKeys[j];
					e=tr.select("td:eq(0) table#left table:gt(0)");
					for(Element es:e){
						String key_s=es.attr("id").split("_")[2];
						if(key_s.equals(keys) ){
							outboundTb.add(es); 
							break;
						}else{
							continue;
						}
					}
					
				 }
			 }
			 
			
//			 outboundTb= tr.select("td:eq(0) table#left table:gt(0)");
//			 inboundTb=tr.select("td:eq(2) > table#right table:gt(0)");
			 
		 }else{		
			 inboundTb=tr.select("td:eq(2) > table#right table:gt(0)");
			 outboundTb= tr.select("td:eq(0) table#left table:gt(0)");
		 }
		 
		 flightInfoAssembly(outboundTb, outbound);
		 flightInfoAssembly(inboundTb, inbound);
		 
		 flights.put("outbound", outbound);
		 flights.put("inbound", inbound);
		 flights.put("binType", binType);
		 
		 return flights;
	}
	
	/**
	 * 航班信息组装，这里只有大致信息可解析，具体详细还要请求后才能获取
	 * 
	 * @param elements
	 * @param list
	 */
	private void flightInfoAssembly( Elements elements, List<Map<String, String>> list) {
		String crossover = null;
		
		 for(Element element : elements) {
			 Map<String, String> flight = new HashMap<String, String>();
			 flight.put("id", element.attr("id").split("_")[2]);
			 flight.put("lowPrice", element.select("td:eq(0)").attr("onmouseover").split(";")[1].replace("'", ""));
			 flight.put("fromDate", element.select("td:eq(2) tr:eq(0) td:eq(0)").html());
			 flight.put("toDate", element.select("td:eq(2) tr:eq(1) td:eq(0)").html());
			 crossover = element.select("td:eq(2) tr:eq(1) td:eq(1) div").text();
			 flight.put("fromAddress", element.select("td:eq(2) tr:eq(0) td:eq(1)").text());
			 flight.put("toAddress", element.select("td:eq(2) tr:eq(1) td:eq(1)").text());
			 flight.put("companyAndFlight", element.select("td:eq(3)").text());
			 flight.put("duration", element.select("tr:eq(1) ul:eq(0) li:eq(0)").text());
			 flight.put("stoppingPoint", element.select("tr:eq(1) ul:eq(0) li:eq(2)").text());
			 
			 if (StringUtils.isNotBlank(crossover)) {
				 flight.put("toDate", flight.get("toDate").concat(crossover.trim().replaceAll("天", "")));
			 }
			
			 list.add(flight);
			 
			log.debug(flight.toString());
		 }
		 
		log.debug("===============================================");
	}
	
	private boolean isValidDetail(String detailHtml) {
		Document doc = Jsoup.parse(detailHtml);

		// 请求是否异常，如果异常则中止
		if (doc.select("#sh_fltItinerary table").isEmpty()) {
			log.warn("请求反回内容非想要结果");
			return false;
		} 
		
		return true;
	}
	
	/**
	 * 详细页数据解析组装
	 * 
	 * @param detailHtml
	 * @param dpie
	 */
	private void flightDetailAssembly(String detailHtml, DoublePlaneInfoEntity dpie, ReturnDoublePlaneInfoEntity rdpe, String cabinType) {
		Document doc = Jsoup.parse(detailHtml);
		Elements outDetail = doc.select("div#sh_fltItinerary #tabFgtReview_0 tr");
		Elements inDetail = doc.select("div#sh_fltItinerary #tabFgtReview_1 tr");
		Elements price =  doc.select("div.sectionHolder table.tableConfText tr.fared2");
		
		try {
			detailAssembly(outDetail, dpie, rdpe, FlightType.OUTBOUND, dpie.getTransits().isEmpty());
			detailAssembly(inDetail, dpie, rdpe, FlightType.INBOUND, true);
			
			// 判断是否有中转，如果中转只有一条数据则不存在中转
			if (dpie.getTransits().size() < 2) {
				dpie.getTransits().clear();
			}
			
			if (rdpe.getReturnTransits().size() < 2) {
				rdpe.getReturnTransits().clear();
			}
			
			// 航班类型.机场等补全
			if (!dpie.getTransits().isEmpty()) {
				dpie.setFlightType(dpie.getTransits().get(0).getFlightType());
			}
			
			// 全价组合
			CabinEntity cabin = getOutboundCabin(dpie, cabinType);
			dpie.getCabins().add(cabin);
			ReturnCabinEntity rcabin =  PlaneInfoEntityBuilder.buildCabinInfo(cabinType, cabinType, null, null, null, null, null, ReturnCabinEntity.class);
			rdpe.getReturnCabins().add(rcabin);
			CabinRelationEntity cre = new CabinRelationEntity();
			cre.setCabinId(cabin.getId());
			cre.setReturnCabinId(rcabin.getId());
			cre.setFullPrice(Double.parseDouble(price.select("td:eq(2)").text().replaceAll("[\\(,]","")));
			cre.setTaxesPrice(Double.parseDouble(price.select("td #taxes_ADT").text().replaceAll("[\\),]","")));
			cre.setTotalFullPrice(Double.parseDouble(price.select("td #totalForATravellerType_ADT").text().replaceAll("[(CNY),]","")));
			dpie.getCabinRelations().add(cre);
			dpie.getReturnPlaneInfos().add(rdpe);
			
			log.debug("抓取封装后结果：{}" , dpie);
		} catch (Exception e) {
			log.error("详细获取出错：", e);
		}
		
		log.debug("裸价：{}, 税费：{}, 总价：{}", new Object[]{price.select("td:eq(2)").text(), price.select("td #taxes_ADT").text(), price.select("td #totalForATravellerType_ADT").text()});
	}
	
	/**
	 * 详细页面数据组装
	 * 
	 * @param elements
	 * @param dpie
	 * @param rdpe
	 * @param type
	 * @throws Exception 
	 */
	private void detailAssembly(Elements elements, DoublePlaneInfoEntity dpie, ReturnDoublePlaneInfoEntity rdpe, FlightType type, boolean transitsIsEmpty) throws Exception {
		Elements content = null;
		Elements changeAirport = null;
		int typeId = type == FlightType.OUTBOUND ? 0 : 1; 
		int id = 0;
		String fromDate = null;
		String fromAddress = null;
		String toDate = null;
		String toAddress = null;
		String flightType = null;
		String airline = null;
		String flightNo = null;
		String carrierName = null;
		String faretype = null;
		String dining = null;
		String currDate = null;
				
		for (Element element : elements) {
			content = element.select("td:eq(1) > table");
			changeAirport = element.select("div");
			
			if (!content.isEmpty()) {
				fromDate = content.select("tr:eq(0) table tr:eq(0) td:eq(1)").text().replaceAll("[天\\s]", "");
				fromAddress = content.select("tr:eq(0) table tr:eq(0) td:eq(2)").text();
				toDate = content.select("tr:eq(0) table tr:eq(1) td:eq(1)").text().replaceAll("[天\\s]", "");
				toAddress = content.select("tr:eq(0) table tr:eq(1) td:eq(2)").text();
				flightType = content.select("tr:eq(2) td#segAircraft_" + typeId + "_" + id).text();
				airline = content.select("td#segAirline_" + typeId + "_" + id).text().trim();       //航空公司
				// 这里不是空格，不知道是什么特殊字符也无法用正则匹配空白字符
				if (airline.lastIndexOf(" ") != -1) {
					flightNo = airline.substring(airline.lastIndexOf(" ") + 1);
					carrierName = airline.substring(0, airline.lastIndexOf(" "));
				} else {
					flightNo = airline;
					carrierName = airline;
				}
				
				faretype = content.select("tr#faretype td:eq(1)").text();      //仓位类型
				dining = content.select("#segMeal_" + typeId + "_" + id).text();       // 用餐类型
				currDate = content.get(0).parent().parent().previousElementSibling().select("td:eq(1)").text();
				currDate = dateFormate(currDate);
				
				//  中转信息
				if (type == FlightType.OUTBOUND) {
					// 去程每次左边与右边匹配时，中转就一样都为同一去程，所以只存放一次
					if (transitsIsEmpty) {
						TransitEntity te = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, null, flightNo.substring(0,2), carrierName, carrierName, null, fromAddress, null, toAddress, flightType, TransitEntity.class);
						dpie.getTransits().add(te);
						te.setStartTime(PlaneInfoEntityBuilder.getFlightTime(currDate, fromDate));
						te.setEndTime(PlaneInfoEntityBuilder.getFlightTime(currDate, toDate));
						te.setCabinName(faretype);
					}
					
				
				} else {
					ReturnTransitEntity rte = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, null, flightNo.substring(0,2), carrierName, carrierName, null, fromAddress, null, toAddress, flightType, ReturnTransitEntity.class);
					rdpe.getReturnTransits().add(rte);
					rte.setStartTime(PlaneInfoEntityBuilder.getFlightTime(currDate, fromDate));
					rte.setEndTime(PlaneInfoEntityBuilder.getFlightTime(currDate, toDate));
				}
				
				log.debug("currDate:{}", currDate);
				log.debug("fromDate:{}", fromDate);
				log.debug("fromAddress:{}", fromAddress);
				log.debug("toDate:{}", toDate);
				log.debug("toAddress:{}", toAddress);
				log.debug("flightType:{}", flightType);
				log.debug("airline:{}", airline);
				log.debug("flightNo:{}", flightNo);
				log.debug("carrierName:{}", carrierName);
				log.debug("faretype:{}", faretype);
				log.debug("dining:{}", dining);
				
				id++;
				log.debug("----------------------------");
			} else if(!changeAirport.isEmpty()) {
				long dwellTime = timeParse(changeAirport.text().split("=")[1]);
				
				// 中转停留时间
				if (type == FlightType.OUTBOUND) {
					dpie.getTransits().get(dpie.getTransits().size() - 1).setStayTime(dwellTime);
				} else {
					rdpe.getReturnTransits().get(rdpe.getReturnTransits().size() - 1).setStayTime(dwellTime);
				}
				
				log.debug("dwellTime:{}", dwellTime);
				log.debug("----------------------------");
			}
		}
		
		log.debug("==========================================================");
	}
	
	
	/**
	 * 先将抓取到的数据取入
	 * 
	 * @param bound		去或回程信息
	 * @throws Exception 
	 */
	private DoublePlaneInfoEntity offlineFlightInfoAssembly(Map<String, String> bound) throws Exception {
		DoublePlaneInfoEntity dpie = null;
		String caf = bound.get("companyAndFlight");
		String duration = bound.get("duration");
		String fromDate = bound.get("fromDate");
		String toDate = bound.get("toDate");
		String lowPrice = bound.get("lowPrice");
		String carrierName = caf.substring(0, caf.indexOf("("));
		String flightNo = caf.substring(caf.indexOf("(") + 1, caf.indexOf(")"));
		dpie = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, flightNo.substring(0, 2), carrierName, carrierName, fromDate, toDate, flightNo, null, null, null, null, DoublePlaneInfoEntity.class);
		dpie.setCurrency("CNY");
		dpie.setFlightDuration(timeParse(duration));
		dpie.setSumLowestPrice(Double.parseDouble(lowPrice.replaceAll("[\\),]","")));
		
		return dpie;
	}
	
	/**
	 * 先将抓取到的数据取入
	 * 
	 * @param bound		去或回程信息
	 * @throws Exception 
	 */
	private ReturnDoublePlaneInfoEntity offlineFlightReturnInfoAssembly(Map<String, String> bound) throws Exception {
		ReturnDoublePlaneInfoEntity rdpie = null;
		String caf = bound.get("companyAndFlight");
		String duration = bound.get("duration");
		String fromDate = bound.get("fromDate");
		String toDate = bound.get("toDate");
		String lowPrice = bound.get("lowPrice");
		String carrierName = caf.substring(0, caf.indexOf("("));
		String flightNo = caf.substring(caf.indexOf("(") + 1, caf.indexOf(")"));
		rdpie = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, flightNo.substring(0, 2), carrierName, carrierName, fromDate, toDate, flightNo, null, null, null, null, ReturnDoublePlaneInfoEntity.class);
		rdpie.setCurrency("CNY");
		rdpie.setFlightDuration(timeParse(duration));
		rdpie.setSumLowestPrice(Double.parseDouble(lowPrice.replaceAll(",", "").replaceAll("\\)", "")));
		
		return rdpie;
	}
	
	/**
	 * 网站上js代码翻译成了java代码使用，主要用于获取commendationId
	 * 被翻译js脚本 ：
	 * <pre>
	 * var associatedRecos = this._listRecos[this.SelectedRecommendationID].associatedRecos;
	 * if (associatedRecos != null) {
	 * 	for (var i = 0; i &lt; associatedRecos.length; i++) {
	 * 		var lTrueID = associatedRecos[i];
	 * 		if (this._listRecos[lTrueID].outboundFlightIDs
	 * 				.indexOf('|' + lFlightOut + '|') &gt;= 0) {
	 * 			if (this.MaxIDInbound &gt; 0) {
	 * 				if (this._listRecos[lTrueID].inboundFlightIDs
	 * 						.indexOf('|' + lFlightIn + '|') &gt;= 0) {
	 * 					return lTrueID;
	 * 				}
	 * 			} else {
	 * 				return lTrueID;
	 * 			}
	 * 		}
	 * 	}  
	 * }
	 * return this.SelectedRecommendationID;
	 * </pre>
	 * 
	 * @param selectedRecommendationID
	 * @param lFlightOut
	 * @param lFlightIn
	 * @return
	 */
	private String getRecommendationId(String selectedRecommendationID, String lFlightOut, String lFlightIn) {
		Map<String, Object> listRecos = (Map<String, Object>) json.get("listRecos");
		int maxIDInbound =  Integer.parseInt(json.get("maxIDInbound").toString());
		Map<String, Object> recos = (Map<String, Object>)listRecos.get(selectedRecommendationID);
		String recommendationId = selectedRecommendationID;
		Map<String, Object> recosSecond = null;
		
	    List<Integer> associatedRecos = (List<Integer>) recos.get("associatedRecos");
	    
	    if (!(associatedRecos == null || associatedRecos.isEmpty())) {
	    	for (Integer iTrueID : associatedRecos) { 
	    		recosSecond = (Map<String, Object>)listRecos.get(iTrueID.toString());
	    		if (StringUtils.contains(recosSecond.get("outboundFlightIDs").toString(), "|" + lFlightOut + "|")) {
	    			if (maxIDInbound <= 0) {
	    				recommendationId = iTrueID.toString();
	    				break;
	    			}
	    			
	    			if (StringUtils.contains(recosSecond.get("inboundFlightIDs").toString(), "|" + lFlightIn + "|")) {
	    				recommendationId = iTrueID.toString();
	    				break;
	    			}
	    		}
	    	}
	    }
	    
	    return recommendationId;
	}
	
	// 2014年7月19日星期六等类型数据
	private String dateFormate(String date) {
		String dateStr = null;
		Pattern pattern = Pattern.compile("(\\d{1,})");
		Matcher matcher = pattern.matcher(date);
		
		while (matcher.find()) {
			if (dateStr == null) {
				dateStr = matcher.group();
			} else {
				dateStr += "-" + matcher.group();
			}
		}
		
		dateStr = curr_sdf.format(DateUtils.parseDate(dateStr));
		
		return dateStr;
	}
	
	/**
	 * 
	 * 
	 * @param time xx小时xx分 或 xx分
	 * @return
	 */
	private long timeParse(String date) {
		long time = 0;
		List<Integer> dates = new ArrayList<Integer>();
		Pattern pattern = Pattern.compile("(\\d{1,})");
		Matcher matcher = pattern.matcher(date);
		
		while (matcher.find()) {
			dates.add(Integer.parseInt(matcher.group()));
		}
		
		if (dates.size() == 2) {
			time += dates.get(0) * 60 * 60 * 1000;
			time += dates.get(1) * 60 * 1000;
		} else {
			time += dates.get(0) * 60 * 1000;
		}
		
		return time;
	}
	
	/**
	 * 进入页面时首次查询动作参数
	 * 
	 * @return
	 */
	private Map<String, String> getFirstQueryParams() {
		Map<String, String> params = new HashMap<String, String>();
		String flightDate = sdf.format(DateUtil.StringToDate("yyyy-MM-dd",taskQueue.getFlightDate()));
		String returnGrabDate = sdf.format(DateUtil.StringToDate("yyyy-MM-dd", taskQueue.getReturnGrabDate()));
		params.put("ARRANGE_BY", "N");
		params.put("B_ANY_TIME_1", "TRUE");
		params.put("B_ANY_TIME_2", "TRUE");
		params.put("B_DATE_1", flightDate);
		params.put("B_DATE_2", returnGrabDate);
		params.put("B_LOCATION_1", taskQueue.getFromCity().toLowerCase());
		params.put("B_LOCATION_2", taskQueue.getToCity().toLowerCase());
		params.put("COMMERCIAL_FARE_FAMILY_1", "ECONOMYCN");
		params.put("DATE_RANGE_QUALIFIER_1", "C");
		params.put("DATE_RANGE_QUALIFIER_2", "C");
		params.put("DATE_RANGE_VALUE_1", "3");
		params.put("DATE_RANGE_VALUE_2", "3");
		params.put("DIRECT_NON_STOP", "FALSE");
		params.put("DISPLAY_TYPE", "1");
		params.put("EMBEDDED_TRANSACTION", "FlexPricerAvailability");
		params.put("EXTERNAL_ID", "CHINA");
		params.put("E_LOCATION_1", taskQueue.getToCity().toLowerCase());
		params.put("E_LOCATION_2", taskQueue.getFromCity().toLowerCase());
		params.put("FP_ERT_ACTIVATED", "FALSE");
		params.put("LANGUAGE", "CN");
		params.put("PLTG_FROMPAGE", "ADVS");
		params.put("PRICING_TYPE", "I");
		params.put("REFRESH", "0");
		params.put("SEVEN_DAY_SEARCH", "TRUE");
		params.put("SITE", "CBHSCBHS");
		params.put("SO_SITE_AIRLINE_LOC_WAIT", "30000");
		params.put("SO_SITE_ALLOW_AUTOMATED_TKT", "TRUE");
		params.put("SO_SITE_ALLOW_LSA_INDICATOR", "TRUE");
		params.put("SO_SITE_ALLOW_PROMO", "FALSE");
		params.put("SO_SITE_AMADEUS_OFFICE_ID", "BJSAA18AB");
		params.put("SO_SITE_CORPORATE_ID", "OCG-MUCWW28AA");
		params.put("SO_SITE_CURRENCY_FORMAT", "%d");
		params.put("SO_SITE_CURRENCY_FORMAT_JAVA", "0");
		params.put("SO_SITE_DISPLAY_NBR_OF_LSA", "TRUE");
		params.put("SO_SITE_MINIMAL_TIME", "H4");
		params.put("SO_SITE_MIN_AVAIL_DATE_SPAN", "H4");
		params.put("SO_SITE_OFFICE_ID", "BJSAA18AB");
		params.put("SO_SITE_POINT_OF_SALE", "BJS");
		params.put("SO_SITE_PREFERRED_CARRIER", "AAYY");
		params.put("SO_SITE_UI_FLIFO_DISP_DOT", "TRUE");
		params.put("TRAVELLER_TYPE_1", "ADT");
		params.put("TRIP_FLOW", "YES");
		params.put("TRIP_TYPE", "R");

		return params;
	}
	
	/**
	 * 去程与回程组合详细页面请请参数
	 * 
	 * @param outbound  去程id
	 * @param inbound   回程id
	 * 
	 * @return
	 */
	private Map<String, String> getFlightPrice(String outbound, String inbound, String familyButton) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("0fdffRadioOut", "radOut");
		params.put("1fdffRadioOut", "radOut");
		params.put("BANNER", null);
		params.put("DISPLAY_TYPE", "1");
		params.put("FLIGHTINBOUND", inbound);
		params.put("FLIGHTOUTBOUND", outbound);
		params.put("FLIGHT_ID_1", outbound);
		params.put("FLIGHT_ID_2", inbound);
		params.put("FamilyButton", familyButton);
		params.put("IS_DIRTY_INBOUND", "Y");
		params.put("IS_DIRTY_OUTBOUND", "Y");
		params.put("LANGUAGE", "CN");
		params.put("PAGE_TICKET", "1");
		params.put("PRICING_TYPE", "I");
		params.put("RECOMMENDATION_ID_1", getRecommendationId(familyButton.split("\\|")[0] , outbound, inbound));
		params.put("SITE", "CBHSCBHS");
		params.put("SKIN", null);
		params.put("TRIP_TYPE", "R");
		params.put("selectSortFlights_out", "N");
		
		return params;
	}
	
	private CabinEntity getOutboundCabin(DoublePlaneInfoEntity  dpie, String cabinType) throws Exception {
		for (CabinEntity ce : dpie.getCabins()) {
			if (ce.getCabinType().equals(cabinType)) {
				return ce;
			}
		}
		
		return PlaneInfoEntityBuilder.buildCabinInfo(cabinType, cabinType, null, null, null, null, null, CabinEntity.class);
	}

	public static void main(String[] args) throws Exception {
	/*	String airline = "American Airlines AA187";
		System.out.println(Arrays.toString(airline.split("^\\W+|\\W+$| ")));
		System.out.println(airline.substring(airline.lastIndexOf(" ") + 1));
		System.out.println(airline.substring(0, airline.lastIndexOf(" ")));
		System.exit(0);*/
		ConfigContainer.getInstance().register(ProxyipProperties.class);
		TaskModel tm = new TaskModel();
		tm.setFromCity("PVG"); 
		tm.setToCity("LAX");
		//tm.setFromCity("nrt");
		//tm.setToCity("jfk");
		tm.setFlightDate("2015-01-20");
		tm.setReturnGrabDate("2015-04-20");
		tm.setIsInternational(1);
		tm.setIsReturn(1);
		tm.setFromCityName("");
		tm.setToCityName("");
		AmericanairlinesAdapter ala = new AmericanairlinesAdapter(tm);

	/*	InputStream input = AmericanairlinesAdapter.class.getClassLoader().getResourceAsStream("data.html");
		StringBuffer buffer = new StringBuffer();
		byte[] tmp = new byte[1024];
		int l;
		while ((l = input.read(tmp)) != -1) {
			buffer.append(new String(tmp, 0, l,"gbk"));
		}*/
		
		//ala.flightDetailAssembly(buffer.toString(), null);
		//ala.jsonAnalysis(buffer.toString());
		//ala.paraseToVo(buffer.toString());
		//ala.flightDetailAssembly(buffer.toString(), new DoublePlaneInfoEntity(), new ReturnDoublePlaneInfoEntity(), "经济仓");
		// ala.timeParse("32小时05分");        
		//System.out.println(ala.execute(ala.detailUrl, null, Reqeust.POST));
		String fight = (String) ala.fetch(null);
		if (ala.validateFetch(fight)) { 
			ala.paraseToVo(fight);
		}
		//ala.paramsCreate("tripTypeRadio=roundTrip&origin_display=%E5%8C%97%E4%BA%AC%E9%A6%96%E9%83%BD(BJS)+%E5%8C%97%E4%BA%AC%E9%A6%96%E9%83%BD%E6%9C%BA%E5%9C%BA(BJS)+%E4%B8%AD%E5%9B%BD&origin1=BJS&aa-leavingOn=2014-07-19&flightParams.flightDateParams.travelMonth=7&flightParams.flightDateParams.travelDay=19&destination_display=%E7%BA%BD%E7%BA%A6%E5%B8%82(NYC)+%E7%BE%8E%E5%9B%BD%E7%BA%BD%E7%BA%A6%E5%B7%9E&destination1=NYC&aa-returningFrom=2014-07-20&returnDate.travelMonth=7&returnDate.travelDay=20&numberOfAdults=1&numberOfChildren=0&locale=zh_CN&departureTime1=ANY&departureTime2=ANY&classOfService=E&flowType=flexPricer&searchType=flexSearch&numberOfInfants=0&tripType=R&date1=19%2F7%2F2014&date2=20%2F7%2F2014");
		//ala.jspRequest();
		//ala.paramsCreate("0fdffRadioOut=radOut&1fdffRadioOut=radOut&BANNER=empt&DISPLAY_TYPE=1&FLIGHTINBOUND=1&FLIGHTOUTBOUND=0&FLIGHT_ID_1=0&FLIGHT_ID_2=1&FamilyButton=28%7C1&IS_DIRTY_INBOUND=Y&IS_DIRTY_OUTBOUND=Y&LANGUAGE=CN&PAGE_TICKET=1&PRICING_TYPE=I&RECOMMENDATION_ID_1=28&SITE=CBHSCBHS&SKIN=empt&TRIP_TYPE=R&selectSortFlights_out=N");
	}
}
